﻿// //   Copyright 2007-2011 Comdiv (F. Sadykov) - http://code.google.com/u/fagim.sadykov/
// //   Supported by Media Technology LTD 
// //    
// //   Licensed under the Apache License, Version 2.0 (the "License");
// //   you may not use this file except in compliance with the License.
// //   You may obtain a copy of the License at
// //    
// //        http://www.apache.org/licenses/LICENSE-2.0
// //    
// //   Unless required by applicable law or agreed to in writing, software
// //   distributed under the License is distributed on an "AS IS" BASIS,
// //   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// //   See the License for the specific language governing permissions and
// //   limitations under the License.
// //   
// //   MODIFICATIONS HAVE BEEN MADE TO THIS FILE

using System;
using System.Diagnostics;
using System.Web;
using Comdiv.QWeb.Cache;
using Comdiv.QWeb.Diagnostics;
using Comdiv.QWeb.Security;

namespace Comdiv.QWeb {
// ReSharper disable ClassWithVirtualMembersNeverInherited.Global
	public class QWebHandler : IHttpHandler {
// ReSharper restore ClassWithVirtualMembersNeverInherited.Global
		private readonly IActionAuthorizer _authorizer;
		private readonly QWebServiceRegistry _registry;
		private readonly IQWebStatistics _stats;

		public QWebHandler() : this(QWebServiceRegistry.Default) {
		}


		public QWebHandler(QWebServiceRegistry registry) {
			_registry = registry;
			_stats = registry.Statistics;
			_authorizer = registry.GetNew<IActionAuthorizer>();
			_isDefaultHandler = true;
		}

		private bool _isDefaultHandler;

		#region IHttpHandler Members

		

		public bool IsReusable {
			get { return true; }
		}

	
		#endregion

		public void ProcessRequest(HttpContext context) {
			_isDefaultHandler = true;
			ProcessRequest(new QWebContext(new HttpContextWrapper(context)));
		}

		public QWebContext ProcessRequest(QWebContext context) {
			_registry.Synchronize();
			_registry.PrincipalSource.SetCurrentUser(context.LogonUser);
			SetupContext(context);
			OnStart(context);
			try {
				context.AuthrizeResult = Authorize(context);
				if (context.AuthrizeResult.Authorized) {
					if (!context.IgnoreActionResult) {
						var s = Stopwatch.StartNew();
						try {
							CheckNotModified(context);
							if (!context.NotModified) {
								BeforeAction(context);
								context.Process();
								AfterAction(context);
							}
						}
						finally {
							s.Stop();
							_stats.RegisterExecuteTime(context.ActionName, s.ElapsedMilliseconds);
						}
					}
					else {
						CheckNotModified(context);
						context.AuthrizeResult = Authorize(context);
					}
					if (context.AuthrizeResult.Authorized) {
						if (!context.NotModified) {
							BeforeRender(context);
							var s = Stopwatch.StartNew();
							context.Render();
							s.Stop();
							_stats.RegisterRenderTime(context.RenderType, s.ElapsedMilliseconds);
							AfterRender(context);
							context.Output.Flush();
							context.Output.Close();
						}
						if (context.NotModified) {
							processNotModifiedState(context);
						}
						else {
							SetModifiedHeader(context);
						}
					}
				}
				else {
					context.StatusCode = 403;
					context.Error = context.AuthrizeResult.AuthorizeError;
					context.RenderError(context.AuthrizeResult.AuthorizeError);
				}
			}
			catch (Exception ex) {
				context.StatusCode = 403;
				context.Error = ex;
				context.RenderError(ex);
				OnError(ex);
			}
			finally {
				context.Release();
				_registry.PrincipalSource.SetCurrentUser(null);
			}
			OnFinish(context);
			return context;
		}

		private void CheckNotModified(QWebContext context) {
			context.NotModified = canReturnNotModifiedStatus(context);
			if (context.NotModified) {
				_stats.RegisterNotModified(context.ActionName);
			}
		}

		private void SetupContext(QWebContext context) {
			context.Registry = _registry;
			if (_isDefaultHandler) QWebContext.Current = context;
			context.Setup();
			context.NotModified = false;
		}

		private static void SetModifiedHeader(QWebContext context) {
			INotModifiedStateProvider cp = null;
			if (null != context.RenderDescriptor && context.RenderDescriptor.SupportNotModifiedState) {
				cp = context.RenderDescriptor;
			}
			else if (null == context.ActionDescriptor) return;
			cp = cp ?? context.ActionDescriptor;
			if (!cp.SupportNotModifiedState) return;
			context.LastModified = cp.LastModified;
			context.Etag = cp.ETag;
		}

		private void processNotModifiedState(QWebContext context) {
			context.StatusCode = 304;
		}

		private bool canReturnNotModifiedStatus(QWebContext context) {
			if (context.IfModifiedSince.Year <= 1900) return false;
			if (null != context.RenderDescriptor && context.RenderDescriptor.SupportNotModifiedState) {
				var rcp = (INotModifiedStateProvider) context.RenderDescriptor;
				return CheckbyLastModAndEtag(context, rcp.LastModified, rcp.ETag);
			}
			if (null == context.ActionDescriptor) return false;
			var cp = (INotModifiedStateProvider) context.ActionDescriptor;
			if (!cp.SupportNotModifiedState) return false;
			return CheckbyLastModAndEtag(context, cp.LastModified, cp.ETag);
		}

		private static bool CheckbyLastModAndEtag(QWebContext context, DateTime lastmod, string etag) {
			if (context.IfModifiedSince < lastmod) return false;
			if (etag != context.IfNoneMatch) return false;
			return true;
		}

// ReSharper disable UnusedParameter.Global
		protected virtual void OnError(Exception exception) {

		}

		protected virtual void AfterRender(QWebContext context)
		{
		}

		protected virtual void BeforeRender(QWebContext context)
		{
		}

		protected virtual void AfterAction(QWebContext context)
		{
		}

		protected virtual void BeforeAction(QWebContext context)
		{
		}

		protected virtual void OnFinish(QWebContext context)
		{
		}

		protected virtual void OnStart(QWebContext context) {
		}
// ReSharper restore UnusedParameter.Global
		protected virtual AuthorizationResult Authorize(QWebContext context) {
			return _authorizer.Authorize(context);
		}
	}
}